import gc
import os

from pyroute2 import Ethtool
from pyroute2.ethtool.module_info import (
    ModuleInfoParser,
    sff8472_get_diag_mon_support,
)


def get_fds():
    fd = os.open(f'/proc/{os.getpid()}/fd', os.O_RDONLY)
    try:
        return set(os.listdir(fd)) - {fd}
    finally:
        os.close(fd)


def _test_pipe_leak():
    fds = get_fds()
    etht = Ethtool()
    etht.close()
    gc.collect()
    assert get_fds() == fds


def _test_context_manager():
    fds = get_fds()
    with Ethtool():
        pass
    gc.collect()
    assert get_fds() == fds


def test_module_info_sff8079_sfp():
    """Test decoding of module information for a SFF 8079 SFP module"""
    # fmt: off
    eeprom = (
        0x03, 0x04, 0x07, 0x10, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x06, 0x67, 0x00, 0x00, 0x00,
        0x08, 0x02, 0x00, 0x1e, 0x50, 0x75, 0x72, 0x65,
        0x6f, 0x70, 0x74, 0x69, 0x63, 0x73, 0x20, 0x20,
        0x20, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0x00,
        0x53, 0x46, 0x50, 0x2b, 0x2d, 0x31, 0x30, 0x47,
        0x2d, 0x53, 0x52, 0x2d, 0x50, 0x4f, 0x20, 0x20,
        0x42, 0x34, 0x20, 0x20, 0x03, 0x52, 0x00, 0x73,
        0x00, 0x1a, 0x00, 0x00, 0x4d, 0x43, 0x43, 0x39,
        0x35, 0x30, 0x33, 0x33, 0x20, 0x20, 0x20, 0x20,
        0x20, 0x20, 0x20, 0x20, 0x32, 0x35, 0x30, 0x36,
        0x32, 0x33, 0x20, 0x20, 0x68, 0xf0, 0x03, 0xbe,
        0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
        0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
        0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
        0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x5a, 0x00, 0xf6, 0x00, 0x55, 0x00, 0xfb, 0x00,
        0x8d, 0xcc, 0x74, 0x04, 0x87, 0x28, 0x7a, 0x44,
        0x1d, 0x4c, 0x01, 0xf4, 0x17, 0x70, 0x03, 0xe8,
        0x2e, 0xe0, 0x06, 0x3c, 0x27, 0x10, 0x07, 0xcb,
        0x4e, 0x20, 0x00, 0xfa, 0x27, 0x10, 0x01, 0xf5,
        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x3f, 0x80, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
        0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
        0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4f,
        0x22, 0xe1, 0x84, 0x2f, 0x0b, 0xbb, 0x17, 0x00,
        0x14, 0xf6, 0xff, 0xff, 0xff, 0xff, 0x08, 0xff,
        0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0xff, 0xff,
        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x00, 0x00,
        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
    )
    # fmt: on

    parsed = ModuleInfoParser.parse_sff8079_page0(eeprom)
    assert sff8472_get_diag_mon_support(eeprom)[1] is True
    parsed += ModuleInfoParser.parse_sff8079_page2(eeprom)

    assert parsed == [
        ("Identifier", "SFP"),
        ("Extended identifier", "GBIC/SFP defined by 2-wire interface ID"),
        ("Connector", "LC"),
        ("Transceiver type", ["10G Ethernet: 10G Base-SR"]),
        ("Encoding", "64B/66B"),
        ("BR, Nominal, in MBd", 10300),
        ("BR margin, max, in %", 0),
        ("BR margin, min, in %", 0),
        ("Rate identifier", "unspecified"),
        ("Length (SMF), in km", 0),
        ("Length (SMF), in m", 0),
        ("Length (50um), in m", 80),
        ("Length (62.5um), in m", 20),
        ("Length (Copper), in m", 0),
        ("Length (OM3), in m", 300),
        ("Laser wavelength, in nm", 850),
        ("Vendor name", "Pureoptics"),
        ("Vendor OUI", "00:00:00"),
        ("Vendor PN", "SFP+-10G-SR-PO"),
        ("Vendor rev", "B4"),
        (
            "Option",
            [
                "RX_LOS implemented",
                "TX_FAULT implemented",
                "TX_DISABLE implemented",
            ],
        ),
        ("Vendor SN", "MCC95033"),
        ("Date code", "250623"),
        ("Optical diagnostics support", True),
        ("Module temperature, in °C", 34.88),
        ("Module voltage, in V", 3.3839),
        ("Laser bias current, in mA", 6.006),
        ("Laser output power, in mW", 0.5888),
        ("Receiver signal, in mW (average optical power)", 0.5366),
        ("Alarm/warning flags implemented", True),
        ("Module temperature high alarm", False),
        ("Module temperature low alarm", False),
        ("Module temperature high warning", False),
        ("Module temperature low warning", False),
        ("Module voltage high alarm", False),
        ("Module voltage low alarm", False),
        ("Module voltage high warning", False),
        ("Module voltage low warning", False),
        ("Laser bias current high alarm", False),
        ("Laser bias current low alarm", False),
        ("Laser bias current high warning", False),
        ("Laser bias current low warning", False),
        ("Laser output power high alarm", False),
        ("Laser output power low alarm", False),
        ("Laser output power high warning", False),
        ("Laser output power low warning", False),
        ("Laser rx power high alarm", False),
        ("Laser rx power low alarm", False),
        ("Laser rx power high warning", False),
        ("Laser rx power low warning", False),
        ("Module temperature high alarm threshold, in °C", 90.00),
        ("Module temperature low alarm threshold, in °C", -10.0),
        ("Module temperature high warning threshold, in °C", 85.00),
        ("Module temperature low warning threshold, in °C", -5.00),
        ("Module voltage high alarm threshold, in V", 3.6300),
        ("Module voltage low alarm threshold, in V", 2.9700),
        ("Module voltage high warning threshold, in V", 3.4600),
        ("Module voltage low warning threshold, in V", 3.1300),
        ("Laser bias current high alarm threshold, in mA", 15.000),
        ("Laser bias current low alarm threshold, in mA", 1.000),
        ("Laser bias current high warning threshold, in mA", 12.000),
        ("Laser bias current low warning threshold, in mA", 2.000),
        ("Laser output power high alarm threshold, in mW", 1.2000),
        ("Laser output power low alarm threshold, in mW", 0.1596),
        ("Laser output power high warning threshold, in mW", 1.0000),
        ("Laser output power low warning threshold, in mW", 0.1995),
        ("Laser rx power high alarm threshold, in mW", 2.0000),
        ("Laser rx power low alarm threshold, in mW", 0.0250),
        ("Laser rx power high warning threshold, in mW", 1.0000),
        ("Laser rx power low warning threshold, in mW", 0.0501),
    ]


def test_module_info_sff8079_rj45():
    """Test decoding of module information for a SFF 8079 RJ45 module"""
    # fmt: off
    eeprom = (
        0x03, 0x04, 0x22, 0x00, 0x00, 0x00, 0x08, 0x00,
        0x00, 0x00, 0x00, 0x01, 0x0d, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x64, 0x00, 0x49, 0x6e, 0x74, 0x65,
        0x6c, 0x20, 0x43, 0x6f, 0x72, 0x70, 0x20, 0x20,
        0x20, 0x20, 0x20, 0x20, 0x00, 0x00, 0x1b, 0x21,
        0x53, 0x46, 0x50, 0x2d, 0x31, 0x47, 0x2d, 0x54,
        0x2d, 0x41, 0x55, 0x54, 0x4f, 0x2d, 0x49, 0x20,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc1, 0x1b,
        0x00, 0x12, 0x00, 0x00, 0x41, 0x34, 0x38, 0x34,
        0x39, 0x37, 0x36, 0x37, 0x20, 0x20, 0x20, 0x20,
        0x20, 0x20, 0x20, 0x20, 0x32, 0x35, 0x30, 0x39,
        0x30, 0x39, 0x20, 0x20, 0x00, 0x00, 0x00, 0x49,
        0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
        0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
        0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
        0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    )
    # fmt: on

    parsed = ModuleInfoParser.parse_sff8079_page0(eeprom)
    assert sff8472_get_diag_mon_support(eeprom)[1] is False

    assert parsed == [
        ("Identifier", "SFP"),
        ("Extended identifier", "GBIC/SFP defined by 2-wire interface ID"),
        ("Connector", "RJ45"),
        ("Transceiver type", ["Ethernet: 1000BASE-T"]),
        ("Encoding", "8B/10B"),
        ("BR, Nominal, in MBd", 1300),
        ("BR margin, max, in %", 0),
        ("BR margin, min, in %", 0),
        ("Rate identifier", "unspecified"),
        ("Length (SMF), in km", 0),
        ("Length (SMF), in m", 0),
        ("Length (50um), in m", 0),
        ("Length (62.5um), in m", 0),
        ("Length (Copper), in m", 100),
        ("Length (OM3), in m", 0),
        ("Laser wavelength, in nm", 0),
        ("Vendor name", "Intel Corp"),
        ("Vendor OUI", "00:1b:21"),
        ("Vendor PN", "SFP-1G-T-AUTO-I"),
        ("Vendor rev", "\x00\x00\x00\x00"),
        ("Option", ["RX_LOS implemented", "TX_DISABLE implemented"]),
        ("Vendor SN", "A4849767"),
        ("Date code", "250909"),
    ]


def test_module_info_sff8079_copper():
    """Test decoding of module information for a SFF 8079 copper module"""
    # fmt: off
    eeprom = (
        0x03, 0x04, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x04, 0x00, 0x00, 0x00, 0x67, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x01, 0x00, 0x43, 0x49, 0x53, 0x43,
        0x4f, 0x2d, 0x50, 0x55, 0x52, 0x45, 0x4f, 0x50,
        0x54, 0x49, 0x43, 0x53, 0x00, 0x00, 0x00, 0x00,
        0x53, 0x46, 0x50, 0x2d, 0x31, 0x30, 0x47, 0x2d,
        0x54, 0x2d, 0x50, 0x4f, 0x20, 0x20, 0x20, 0x20,
        0x30, 0x37, 0x20, 0x20, 0x01, 0x00, 0x00, 0x73,
        0x00, 0x00, 0x00, 0x00, 0x4b, 0x41, 0x48, 0x5a,
        0x34, 0x32, 0x36, 0x33, 0x20, 0x20, 0x20, 0x20,
        0x20, 0x20, 0x20, 0x20, 0x32, 0x34, 0x31, 0x31,
        0x32, 0x39, 0x20, 0x20, 0x00, 0x00, 0x00, 0x70,
        0x80, 0x00, 0x0c, 0xba, 0x14, 0xdc, 0x5d, 0x15,
        0xe0, 0x01, 0x64, 0xaf, 0x84, 0xa9, 0x0b, 0x25,
        0xf2, 0x31, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x89, 0x1d, 0xbd, 0xd2,
        0x43, 0x4f, 0x50, 0x51, 0x41, 0x41, 0x34, 0x4a,
        0x41, 0x42, 0x33, 0x37, 0x2d, 0x30, 0x39, 0x36,
        0x30, 0x2d, 0x30, 0x33, 0x56, 0x30, 0x33, 0x20,
        0x01, 0x00, 0x46, 0x00, 0x00, 0x00, 0x00, 0xcc,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x53, 0x46, 0x50, 0x2d, 0x48, 0x31, 0x30, 0x47,
        0x42, 0x2d, 0x43, 0x55, 0x31, 0x4d, 0x20, 0x20,
        0x20, 0x20, 0x20, 0x20, 0x30, 0x38, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb3,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
    )
    # fmt: on

    parsed = ModuleInfoParser.parse_sff8079_page0(eeprom)
    assert sff8472_get_diag_mon_support(eeprom)[1] is False

    assert parsed == [
        ("Identifier", "SFP"),
        ("Extended identifier", "GBIC/SFP defined by 2-wire interface ID"),
        ("Connector", "Copper pigtail"),
        ("Transceiver type", ["Passive Cable"]),
        ("Encoding", "unspecified"),
        ("BR, Nominal, in MBd", 10300),
        ("BR margin, max, in %", 0),
        ("BR margin, min, in %", 0),
        ("Rate identifier", "unspecified"),
        ("Length (SMF), in km", 0),
        ("Length (SMF), in m", 0),
        ("Length (50um), in m", 0),
        ("Length (62.5um), in m", 0),
        ("Length (Copper), in m", 1),
        ("Length (OM3), in m", 0),
        (
            "Passive Cu compliance",
            "SFF-8431 appendix E [SFF-8472 rev10.4 only]",
        ),
        ("Vendor name", "CISCO-PUREOPTICS"),
        ("Vendor OUI", "00:00:00"),
        ("Vendor PN", "SFP-10G-T-PO"),
        ("Vendor rev", "07"),
        ("Option", []),
        ("Vendor SN", "KAHZ4263"),
        ("Date code", "241129"),
    ]
